///////////////////////////
//
// File: bcf_menu_edge.cpp
//
// Author: Brian Fehrman
//
// Purpose: Contains the class implementation for the Edge Detection Menu
//
///////////////////////////


#include "bcf_img_proc.h"

///////////////////////////
// Function: Rotate_Filter
//
// Author: Brian Fehrman
//
// Purpose: Rotates the elements in a 3x3 matrix clockwise by one position
///////////////////////////
void BCF_Img_Proc::Rotate_Filter( float** filter, int NSIZE)
{
    float **rotated_filter;

    rotated_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        rotated_filter[ row ] = new float[ NSIZE ];
    }

    rotated_filter[0][0] = filter[1][0];
    rotated_filter[0][1] = filter[0][0];
    rotated_filter[0][2] = filter[0][1];

    rotated_filter[1][0] =  filter[2][0];
    rotated_filter[1][1] =  filter[1][1];
    rotated_filter[1][2] =  filter[0][2];

    rotated_filter[2][0] =  filter[2][1];
    rotated_filter[2][1] =  filter[2][2];
    rotated_filter[2][2] =  filter[1][2];

    for( int row = 0; row < NSIZE; row++ )
    {
        for( int col = 0; col < NSIZE; col++ )
        {
            filter[ row ][ col ] = rotated_filter[ row ][ col ];
        }
    }
}

///////////////////////////
// Function: Sobel
//
// Author: Brian Fehrman
//
// Purpose: Performs sobel edge detection in both the x and y directions
///////////////////////////
bool BCF_Img_Proc::Sobel( Image &image, Image &sobel_x, Image &sobel_y )
{
    if ( image.IsNull() ) return false;

    const int NSIZE = 3;
    sobel_x = image;
    sobel_y = image;

    //Create sobel filters for the x and y directions
    float **sobel_x_filter;
    float **sobel_y_filter;

    sobel_x_filter = new float*[ NSIZE ];
    sobel_y_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        sobel_x_filter[ row ] = new float[ NSIZE ];
        sobel_y_filter[ row ] = new float[ NSIZE ];
    }

    sobel_x_filter[0][0] = -1;
    sobel_x_filter[0][1] = -2;
    sobel_x_filter[0][2] = -1;

    sobel_x_filter[1][0] =  0;
    sobel_x_filter[1][1] =  0;
    sobel_x_filter[1][2] =  0;

    sobel_x_filter[2][0] =  1;
    sobel_x_filter[2][1] =  2;
    sobel_x_filter[2][2] =  1;

    sobel_y_filter[0][0] = -1;
    sobel_y_filter[0][1] =  0;
    sobel_y_filter[0][2] =  1;

    sobel_y_filter[1][0] = -2;
    sobel_y_filter[1][1] =  0;
    sobel_y_filter[1][2] =  2;

    sobel_y_filter[2][0] = -1;
    sobel_y_filter[2][1] =  0;
    sobel_y_filter[2][2] =  1;

    Apply_Correlation_Filter( sobel_x, sobel_x_filter, NSIZE );
    Apply_Correlation_Filter( sobel_y, sobel_y_filter, NSIZE );

    for( int row = 0; row < NSIZE; row++ )
    {
       delete sobel_x_filter[ row ];
       delete sobel_y_filter[ row ];
    }

    delete [] sobel_x_filter;
    delete [] sobel_y_filter;

    return true;
}

///////////////////////////
// Function: Edge_Kirsch_Mag
//
// Author: Brian Fehrman
//
// Purpose: Generates 8 Kirsch Responses
///////////////////////////
bool BCF_Img_Proc::Kirsch( Image &image, Image kirsch_responses[ 8 ] )
{
    if ( image.IsNull() ) return false;

    const int NSIZE = 3;

    //Create sobel filters for the x and y directions
    float **kirsch_filter;

    kirsch_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        kirsch_filter[ row ] = new float[ NSIZE ];
    }

    kirsch_filter[0][0] =  3;
    kirsch_filter[0][1] =  3;
    kirsch_filter[0][2] = -5;

    kirsch_filter[1][0] =  3;
    kirsch_filter[1][1] =  0;
    kirsch_filter[1][2] = -5;

    kirsch_filter[2][0] =  3;
    kirsch_filter[2][1] =  3;
    kirsch_filter[2][2] = -5;

    for( int response = 0; response < 8; response++ )
    {
        kirsch_responses[ response ] = image;
        Apply_Correlation_Filter( kirsch_responses[ response ], kirsch_filter, NSIZE );
        Rotate_Filter( kirsch_filter, NSIZE );
    }

    for( int row = 0; row < NSIZE; row++ )
    {
       delete kirsch_filter[ row ];
    }

    delete [] kirsch_filter;

    return true;
}

///////////////////////////
// Function: Edge_Kirsch_Dir
//
// Author: Brian Fehrman
//
// Purpose: Gets the Max Kirsch Response Direction of the Image
///////////////////////////
bool BCF_Img_Proc::Menu_Edge_Kirsch_Dir( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image kirsch_responses[ 8 ];

    Menu_Filter_Smooth( image );

    //Get sobel response in x and y directions
    Kirsch( image, kirsch_responses );

    //Traverse each pixel in matrix
    for( int img_row= 0; img_row < nrows; img_row++ )
    {
        for( int img_col = 0; img_col < ncols; img_col++ )
        {
            float max = -1000.0;
            int max_response = 0;

            for( int response = 0; response < 8; response++ )
            {
                if( kirsch_responses[ response ][ img_row ][ img_col ] > max )
                {
                    max = kirsch_responses[ response ][ img_row ][ img_col ];
                    max_response = response;
                }
            }

            image[ img_row ][ img_col ] = max_response / 7.0 * 255.0;

        }//end img_col loop

    }//end img_row loop

    return true;
}

///////////////////////////
// Function: Edge_Kirsch_Mag
//
// Author: Brian Fehrman
//
// Purpose: Gets the Max Kirsch Response of the Image
///////////////////////////
bool BCF_Img_Proc::Menu_Edge_Kirsch_Mag( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image kirsch_responses[ 8 ];

    Menu_Filter_Smooth( image );

    //Get sobel response in x and y directions
    Kirsch( image, kirsch_responses );

    //Traverse each pixel in matrix
    for( int img_row= 0; img_row < nrows; img_row++ )
    {
        for( int img_col = 0; img_col < ncols; img_col++ )
        {
            float max = -1000.0;

            for( int response = 0; response < 8; response++ )
            {
                if( kirsch_responses[ response ][ img_row ][ img_col ] > max )
                {
                    max = kirsch_responses[ response ][ img_row ][ img_col ];
                }
            }

            max = max / 3.0;

            if( max > 255 )
            {
                max = 255;
            }

            image[ img_row ][ img_col ] = max;

        }//end img_col loop

    }//end img_row loop

    return true;
}

///////////////////////////
// Function: Edge_Laplacioan
//
// Author: Brian Fehrman
//
// Purpose: Performs Laplacian Edge Detecion on the image
///////////////////////////
bool BCF_Img_Proc::Menu_Edge_Laplacian( Image &image )
{
    if ( image.IsNull() ) return false;

    Menu_Filter_Smooth( image );

    const int NSIZE = 3;

    //Create laplacian filter and pass it to the Auto_Correlation_Filter function
    float **laplacian_filter;

    laplacian_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        laplacian_filter[ row ] = new float[ NSIZE ];
    }

    laplacian_filter[0][0] =  0;
    laplacian_filter[0][1] =  1;
    laplacian_filter[0][2] =  0;

    laplacian_filter[1][0] =  1;
    laplacian_filter[1][1] = -4;
    laplacian_filter[1][2] =  1;

    laplacian_filter[2][0] =  0;
    laplacian_filter[2][1] =  1;
    laplacian_filter[2][2] =  0;

    Apply_Correlation_Filter( image, laplacian_filter, NSIZE );

    histogramEqualize( image );

    for( int row = 0; row < NSIZE; row++ )
    {
       delete laplacian_filter[ row ];
    }

    delete [] laplacian_filter;

    return true;
}

///////////////////////////
// Function: Edge_Sobel_Direction
//
// Author: Brian Fehrman
//
// Purpose: Gets the Sobel Response Direction for the image
///////////////////////////
bool BCF_Img_Proc::Menu_Edge_Sobel_Dir( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image sobel_x, sobel_y;

    Menu_Filter_Smooth( image );

    //Get sobel response in x and y directions
    Sobel( image, sobel_x, sobel_y );

    //Traverse each pixel in matrix
    for( int img_row= 0; img_row < nrows; img_row++ )
    {
        for( int img_col = 0; img_col < ncols; img_col++ )
        {
            float x_response = sobel_x[ img_row ][ img_col ];
            float y_response = sobel_y[ img_row ][ img_col ];
            float direction = atan2( y_response, x_response );

            if( direction < 0 )
            {        //Could find a way in the future to just call the already written mean filter function
                //by setting a flag of some sort. Not a big deal right now though; plus this increases
                //portability of the function
                direction += PI_TIMES_2;
            }

            direction /= PI_TIMES_2;

            image[ img_row ][ img_col ] = direction * 255.0;
        }//end img_col loop

    }//end img_row loop

    histogramEqualize( image );

    return true;
}

///////////////////////////
// Function: Edge_Sobel_Mag
//
// Author: Brian Fehrman
//
// Purpose: Gets the Sobel Response Magnitude for the image
///////////////////////////
bool BCF_Img_Proc::Menu_Edge_Sobel_Mag( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image sobel_x, sobel_y;

    Menu_Filter_Smooth( image );

    //Get sobel response in x and y directions
    Sobel( image, sobel_x, sobel_y );

    //Traverse each pixel in matrix
    for( int img_row= 0; img_row < nrows; img_row++ )
    {
        for( int img_col = 0; img_col < ncols; img_col++ )
        {
            float x_response = sobel_x[ img_row ][ img_col ];
            float y_response = sobel_y[ img_row ][ img_col ];

            image[ img_row ][ img_col ] = sqrt( x_response * x_response + y_response * y_response );
        }//end img_col loop

    }//end img_row loop

    histogramEqualize( image );

    return true;
}
